module skid_buffer #(
           parameter width = 8
       )(
           input clk,
           input rst_n,
           input [width - 1: 0] i_data,
           input i_input_valid,
           input i_output_ready,

           output reg [width - 1: 0] o_data,
           output reg o_output_valid,
           output reg o_input_ready,
           output o_accept,
           output o_transmit
       );
parameter empty = 3'b001;
parameter busy = 3'b010;
parameter full = 3'b100;

reg [2: 0] state, next_state;
wire accept, transmit;
reg [width - 1: 0] buffer;

wire buffer_write_en, o_data_write_en;

assign o_accept = accept;
assign o_transmit = transmit;

assign accept = i_input_valid && o_input_ready;
assign transmit = o_output_valid && i_output_ready;

always@(posedge clk or negedge rst_n)
	begin
		if (!rst_n)
			state <= empty;
		else
			state <= next_state;
	end

always@( * )
	begin
		if (!rst_n)
			next_state <= empty;
		else
			case (state)
				empty:
					begin
						if (accept)
							next_state <= busy;
						else
							next_state <= empty;
					end
				busy:
					begin
						if (accept && !transmit)
							next_state <= full;
						else if (!accept && transmit)
							next_state <= empty;
						else
							next_state <= busy;
					end
				full:
					begin
						if (transmit)
							next_state <= busy;
						else
							next_state <= full;
					end
				default:
					next_state <= empty;
			endcase
	end

assign buffer_write_en = (state == full && accept && !transmit);
assign o_data_write_en = (state == empty && accept && !transmit)
       || (state == busy && accept && transmit)
       || (state == full && !accept && transmit);

always@(posedge clk or negedge rst_n)
	begin
		if (!rst_n)
			begin
				o_data <= 0;
				buffer <= 0;
			end
		else
			begin
				if (o_data_write_en)
					begin
						if (state == full)
							begin
								o_data <= buffer;
								o_output_valid <= 1'b1;
							end
						else
							begin
								o_data <= i_data;
								o_output_valid <= 1'b1;
							end
					end
				else if (!o_data_write_en && i_output_ready)
					begin
						if (state == full)
							begin
								o_data <= buffer;
								o_output_valid <= 1'b1;
							end
					end
				else
					o_output_valid <= 1'b0;


			end
	end

always@(posedge clk or negedge rst_n)
	begin
		if (buffer_write_en)
			buffer <= i_data;
	end

always@(posedge clk or negedge rst_n)
	begin
		if (state == full && !transmit)
			o_input_ready <= 1'b0;
		else
			o_input_ready <= 1'b1;
	end
endmodule

